Introduction

Introduction
The aim of this study is to explore the factor structure of HRV indices.
Databases
Glasgow University Database
The GUDB Database (howell2018high?) contains ECGs from 25 subjects. Each subject was recorded performing 5 different tasks for two minutes (sitting, doing a maths test on a tablet, walking on a treadmill, running on a treadmill, using a hand bike). The sampling rate is 250Hz for all the conditions.
The script to download and format the database using the ECG-GUDB Python package by Bernd Porr can be found here.
MIT-BIH Arrhythmia Database
The MIT-BIH Arrhythmia Database [MIT-Arrhythmia; (moody2001impact?)] contains 48 excerpts of 30-min of two-channel ambulatory ECG recordings sampled at 360Hz and 25 additional recordings from the same participants including common but clinically significant arrhythmias (denoted as the MIT-Arrhythmia-x database).
The script to download and format the database using the can be found here.
MIT-BIH Normal Sinus Rhythm Database
This database includes 18 clean long-term ECG recordings of subjects. Due to memory limits, we only kept the second hour of recording of each participant.
The script to download and format the database using the can be found here.
Fantasia Database
The Fantasia database (iyengar1996age?) consists of twenty young and twenty elderly healthy subjects. All subjects remained in a resting state in sinus rhythm while watching the movie Fantasia (Disney, 1940) to help maintain wakefulness. The continuous ECG signals were digitized at 250 Hz. Each heartbeat was annotated using an automated arrhythmia detection algorithm, and each beat annotation was verified by visual inspection.
Procedure
Results
library(tidyverse)
library(easystats)
data <- read.csv("data/data.csv", stringsAsFactors = FALSE) %>%
select(-HRV_ULF, -HRV_VLF) %>% # Empty
filter(Database != "LUDB") # too short recordings, many indices didn't converge
names(data) <- stringr::str_remove(names(data), "HRV_")
Redundant Indices
Remove Equivalent (r higher than .995)
data %>%
correlation::correlation() %>%
filter(abs(r) > 0.995) %>%
arrange(Parameter1, desc(abs(r)))
| C1d |
C1a |
-1 |
-1 |
-1 |
-1.1e+09 |
250 |
0 |
Pearson |
252 |
| C2d |
C2a |
-1 |
-1 |
-1 |
-Inf |
250 |
0 |
Pearson |
252 |
| Cd |
Ca |
-1 |
-1 |
-1 |
-Inf |
250 |
0 |
Pearson |
252 |
| RMSSD |
SDSD |
1 |
1 |
1 |
5.0e+04 |
250 |
0 |
Pearson |
252 |
| RMSSD |
SD1 |
1 |
1 |
1 |
5.0e+04 |
250 |
0 |
Pearson |
252 |
| RMSSD |
SD1d |
1 |
1 |
1 |
5.4e+02 |
250 |
0 |
Pearson |
252 |
| RMSSD |
SD1a |
1 |
1 |
1 |
4.7e+02 |
250 |
0 |
Pearson |
252 |
| SD1 |
SD1d |
1 |
1 |
1 |
5.4e+02 |
250 |
0 |
Pearson |
252 |
| SD1 |
SD1a |
1 |
1 |
1 |
4.7e+02 |
250 |
0 |
Pearson |
252 |
| SD1d |
SD1a |
1 |
1 |
1 |
2.5e+02 |
250 |
0 |
Pearson |
252 |
| SD2 |
SD2a |
1 |
1 |
1 |
2.9e+02 |
250 |
0 |
Pearson |
252 |
| SD2 |
SD2d |
1 |
1 |
1 |
2.0e+02 |
250 |
0 |
Pearson |
252 |
| SDNN |
SDNNa |
1 |
1 |
1 |
7.3e+02 |
250 |
0 |
Pearson |
252 |
| SDNN |
SDNNd |
1 |
1 |
1 |
5.8e+02 |
250 |
0 |
Pearson |
252 |
| SDNNd |
SDNNa |
1 |
1 |
1 |
3.2e+02 |
250 |
0 |
Pearson |
252 |
| SDSD |
SD1 |
1 |
1 |
1 |
Inf |
250 |
0 |
Pearson |
252 |
| SDSD |
SD1d |
1 |
1 |
1 |
5.4e+02 |
250 |
0 |
Pearson |
252 |
| SDSD |
SD1a |
1 |
1 |
1 |
4.7e+02 |
250 |
0 |
Pearson |
252 |
data <- data %>%
select(-SDSD, -SD1, -SD1d, -SD1a, -CVSD) %>% # Same as RMSSD
select(-SDNNd, -SDNNa) %>% # Same as SDNN
select(-SD2d, -SD2a) %>% # Same as SD2
select(-Cd) %>% # Same as Ca
select(-C1d, -C2d) # Same as C1a and C2a
Recording Length
Investigate effect
correlation(data) %>%
filter(Parameter2 == "Recording_Length") %>%
arrange(desc(abs(r)))
Adjust the data for recording length
data <- effectsize::adjust(data, effect="Recording_Length") %>%
select(-Recording_Length)
Gaussian Graphical Model
library(ggraph)
data %>%
correlation::correlation(partial=FALSE) %>%
correlation::cor_to_pcor() %>%
filter(abs(r) > 0.2) %>%
tidygraph::as_tbl_graph(directed=FALSE) %>%
dplyr::mutate(closeness = tidygraph::centrality_closeness(normalized = TRUE),
degree = tidygraph::centrality_degree(normalized = TRUE),
betweeness = tidygraph::centrality_betweenness(normalized = TRUE)) %>%
tidygraph::activate(nodes) %>%
dplyr::mutate(group1 = as.factor(tidygraph::group_edge_betweenness()),
# group2 = as.factor(tidygraph::group_optimal()),
# group3 = as.factor(tidygraph::group_walktrap()),
# group4 = as.factor(tidygraph::group_spinglass()),
group5 = as.factor(tidygraph::group_louvain())) %>%
ggraph::ggraph(layout = "fr") +
ggraph::geom_edge_arc(aes(colour = r, edge_width = abs(r)), strength = 0.1, show.legend = FALSE) +
ggraph::geom_node_point(aes(size = degree, color = group5), show.legend = FALSE) +
ggraph::geom_node_text(aes(label = name), colour = "white") +
ggraph::scale_edge_color_gradient2(low = "#a20025", high = "#008a00", name = "r") +
ggraph::theme_graph() +
guides(edge_width = FALSE) +
scale_x_continuous(expand = expansion(c(.10, .10))) +
scale_y_continuous(expand = expansion(c(.10, .10))) +
scale_size_continuous(range = c(20, 30)) +
scale_edge_width_continuous(range = c(0.5, 2)) +
see::scale_color_material_d(palette="rainbow", reverse=TRUE)

Groups were identified using the tidygraph::group_optimal algorithm.
Factor Analysis
How many factors
cor <- correlation::correlation(data[sapply(data, is.numeric)]) %>%
as.matrix()
n <- parameters::n_factors(data, cor=cor)
n
| 1 |
t |
Multiple_regression |
| 1 |
p |
Multiple_regression |
| 2 |
Acceleration factor |
Scree |
| 2 |
RMSEA |
Fit |
| 3 |
CNG |
CNG |
| 4 |
beta |
Multiple_regression |
| 7 |
R2 |
Scree_SE |
| 8 |
Optimal coordinates |
Scree |
| 8 |
Parallel analysis |
Scree |
| 8 |
Kaiser criterion |
Scree |
| 15 |
SE Scree |
Scree_SE |
| 21 |
BIC |
Fit |
| 27 |
CRMS |
Fit |
| 28 |
TLI |
Fit |
| 29 |
Bentler |
Bentler |
| 35 |
Bartlett |
Barlett |
| 35 |
Anderson |
Barlett |
| 35 |
Lawley |
Barlett |
plot(n) +
see::theme_modern()

Exploratory Factor Analysis (EFA)
efa <- parameters::factor_analysis(data, cor=cor, n=7, rotation="varimax", fm="ml")
print(efa, threshold="max", sort=TRUE)
> # Rotated loadings from Factor Analysis (varimax-rotation)
>
> Variable | ML4 | ML1 | ML3 | ML2 | ML7 | ML5 | ML6 | Complexity | Uniqueness
> ------------------------------------------------------------------------------------------
> SD1SD2 | 0.86 | | | | | | | 1.28 | 0.17
> DFA | -0.79 | | | | | | | 1.24 | 0.31
> CSI | -0.77 | | | | | | | 2.06 | 0.06
> LnHF | 0.72 | | | | | | | 2.22 | 0.20
> HFn | 0.66 | | | | | | | 1.47 | 0.46
> CSI_Modified | -0.64 | | | | | | | 3.47 | 0.12
> LFn | -0.62 | | | | | | | 1.67 | 0.49
> C1a | -0.56 | | | | | | | 1.71 | 0.57
> HF | 0.55 | | | | | | | 1.22 | 0.67
> VHF | 0.54 | | | | | | | 1.47 | 0.65
> HTI | 0.35 | | | | | | | 2.12 | 0.78
> MCVNN | | 0.97 | | | | | | 1.12 | 4.99e-03
> MadNN | | 0.96 | | | | | | 1.06 | 0.05
> IQRNN | | 0.81 | | | | | | 1.15 | 0.29
> pNN50 | | 0.65 | | | | | | 3.08 | 0.11
> CVI | | 0.61 | | | | | | 3.93 | 0.02
> pNN20 | | 0.55 | | | | | | 3.90 | 0.05
> S | | | 0.99 | | | | | 1.00 | 0.02
> TINN | | | 0.97 | | | | | 1.03 | 0.04
> RMSSD | | | 0.92 | | | | | 1.30 | 0.03
> LFHF | | | 0.79 | | | | | 1.45 | 0.25
> PIP | | | | 0.99 | | | | 1.02 | 4.87e-03
> PSS | | | | 0.94 | | | | 1.03 | 0.11
> PAS | | | | 0.79 | | | | 1.46 | 0.24
> LF | | | | -0.40 | | | | 2.79 | 0.68
> RCMSE | | | | | -0.67 | | | 2.49 | 0.21
> CMSE | | | | | -0.67 | | | 2.38 | 0.26
> C2a | | | | | 0.58 | | | 1.91 | 0.49
> AI | | | | | -0.57 | | | 2.48 | 0.31
> PI | | | | | 0.54 | | | 1.68 | 0.62
> Ca | | | | | 0.51 | | | 1.51 | 0.67
> SampEn | | | | | | 0.74 | | 2.38 | 0.09
> ApEn | | | | | | 0.73 | | 1.88 | 0.24
> CorrDim | | | | | | 0.68 | | 2.22 | 0.27
> MSE | | | | | | 0.41 | | 2.00 | 0.75
> MeanNN | | | | | | | 0.64 | 2.25 | 0.37
>
> The 7 latent factors (varimax rotation) accounted for 70.43% of the total variance of the original data (ML4 = 17.93%, ML1 = 10.90%, ML3 = 10.87%, ML2 = 10.17%, ML7 = 9.55%, ML5 = 7.45%, ML6 = 3.57%).
plot(efa) +
see::theme_modern()

Confirmatory Factor Analysis (CFA)
library(lavaan)
model <- parameters::efa_to_cfa(efa, threshold = "max")
cfa <- lavaan::cfa(model, data=data) %>%
parameters::parameters(standardize=TRUE)
> Error in if (ncol(S) == 1L) { : argument is of length zero
| 1 |
ML4 |
=~ |
HTI |
0.91 |
|
|
|
0 |
Loading |
| 2 |
ML4 |
=~ |
HF |
-1.00 |
|
|
|
0 |
Loading |
| 3 |
ML4 |
=~ |
VHF |
-1.00 |
|
|
|
0 |
Loading |
| 4 |
ML4 |
=~ |
LFn |
-1.00 |
|
|
|
0 |
Loading |
| 5 |
ML4 |
=~ |
HFn |
-1.00 |
|
|
|
0 |
Loading |
| 6 |
ML4 |
=~ |
LnHF |
-1.00 |
|
|
|
0 |
Loading |
| 7 |
ML4 |
=~ |
SD1SD2 |
-1.00 |
|
|
|
0 |
Loading |
| 8 |
ML4 |
=~ |
CSI |
-1.00 |
|
|
|
0 |
Loading |
| 9 |
ML4 |
=~ |
CSI_Modified |
1.00 |
|
|
|
0 |
Loading |
| 10 |
ML4 |
=~ |
C1a |
-1.00 |
|
|
|
0 |
Loading |
| 11 |
ML4 |
=~ |
DFA |
-1.00 |
|
|
|
0 |
Loading |
| 12 |
ML1 |
=~ |
MadNN |
0.96 |
|
|
|
0 |
Loading |
| 13 |
ML1 |
=~ |
MCVNN |
-1.00 |
|
|
|
0 |
Loading |
| 14 |
ML1 |
=~ |
IQRNN |
-1.00 |
|
|
|
0 |
Loading |
| 15 |
ML1 |
=~ |
pNN50 |
-1.00 |
|
|
|
0 |
Loading |
| 16 |
ML1 |
=~ |
pNN20 |
-1.00 |
|
|
|
0 |
Loading |
| 17 |
ML1 |
=~ |
CVI |
-1.00 |
|
|
|
0 |
Loading |
| 18 |
ML3 |
=~ |
RMSSD |
0.72 |
|
|
|
0 |
Loading |
| 19 |
ML3 |
=~ |
TINN |
0.86 |
|
|
|
0 |
Loading |
| 20 |
ML3 |
=~ |
LFHF |
0.00 |
|
|
|
0 |
Loading |
| 21 |
ML3 |
=~ |
S |
1.00 |
|
|
|
0 |
Loading |
| 22 |
ML2 |
=~ |
LF |
0.83 |
|
|
|
0 |
Loading |
| 23 |
ML2 |
=~ |
PIP |
-1.00 |
|
|
|
0 |
Loading |
| 24 |
ML2 |
=~ |
PSS |
-1.00 |
|
|
|
0 |
Loading |
| 25 |
ML2 |
=~ |
PAS |
-1.00 |
|
|
|
0 |
Loading |
| 26 |
ML7 |
=~ |
AI |
0.85 |
|
|
|
0 |
Loading |
| 27 |
ML7 |
=~ |
PI |
1.00 |
|
|
|
0 |
Loading |
| 28 |
ML7 |
=~ |
C2a |
1.00 |
|
|
|
0 |
Loading |
| 29 |
ML7 |
=~ |
Ca |
1.00 |
|
|
|
0 |
Loading |
| 30 |
ML7 |
=~ |
CMSE |
1.00 |
|
|
|
0 |
Loading |
| 31 |
ML7 |
=~ |
RCMSE |
1.00 |
|
|
|
0 |
Loading |
| 32 |
ML5 |
=~ |
ApEn |
0.80 |
|
|
|
0 |
Loading |
| 33 |
ML5 |
=~ |
SampEn |
-0.90 |
|
|
|
0 |
Loading |
| 34 |
ML5 |
=~ |
MSE |
0.82 |
|
|
|
0 |
Loading |
| 35 |
ML5 |
=~ |
CorrDim |
-0.06 |
|
|
|
0 |
Loading |
| 36 |
ML6 |
=~ |
MeanNN |
1.00 |
|
|
|
0 |
Loading |
| 80 |
ML4 |
~~ |
ML1 |
0.39 |
|
|
|
0 |
Correlation |
| 81 |
ML4 |
~~ |
ML3 |
-0.07 |
|
|
|
0 |
Correlation |
| 82 |
ML4 |
~~ |
ML2 |
0.76 |
|
|
|
0 |
Correlation |
| 83 |
ML4 |
~~ |
ML7 |
0.39 |
|
|
|
0 |
Correlation |
| 84 |
ML4 |
~~ |
ML5 |
-0.66 |
|
|
|
0 |
Correlation |
| 85 |
ML4 |
~~ |
ML6 |
-0.06 |
|
|
|
0 |
Correlation |
| 86 |
ML1 |
~~ |
ML3 |
-0.03 |
|
|
|
0 |
Correlation |
| 87 |
ML1 |
~~ |
ML2 |
0.46 |
|
|
|
0 |
Correlation |
| 88 |
ML1 |
~~ |
ML7 |
0.48 |
|
|
|
0 |
Correlation |
| 89 |
ML1 |
~~ |
ML5 |
-0.89 |
|
|
|
0 |
Correlation |
| 90 |
ML1 |
~~ |
ML6 |
-0.47 |
|
|
|
0 |
Correlation |
| 91 |
ML3 |
~~ |
ML2 |
-0.07 |
|
|
|
0 |
Correlation |
| 92 |
ML3 |
~~ |
ML7 |
-0.01 |
|
|
|
0 |
Correlation |
| 93 |
ML3 |
~~ |
ML5 |
0.05 |
|
|
|
0 |
Correlation |
| 94 |
ML3 |
~~ |
ML6 |
-0.01 |
|
|
|
0 |
Correlation |
| 95 |
ML2 |
~~ |
ML7 |
0.33 |
|
|
|
0 |
Correlation |
| 96 |
ML2 |
~~ |
ML5 |
-0.64 |
|
|
|
0 |
Correlation |
| 97 |
ML2 |
~~ |
ML6 |
0.04 |
|
|
|
0 |
Correlation |
| 98 |
ML7 |
~~ |
ML5 |
-0.42 |
|
|
|
0 |
Correlation |
| 99 |
ML7 |
~~ |
ML6 |
-0.32 |
|
|
|
0 |
Correlation |
| 100 |
ML5 |
~~ |
ML6 |
0.42 |
|
|
|
0 |
Correlation |

Cluster Analysis
How many clusters
dat <- effectsize::standardize(data[sapply(data, is.numeric)])
n <- parameters::n_clusters(t(dat), package = c("mclust", "cluster"))
n
| 1 |
Tibs2001SEmax |
cluster |
| 5 |
Mixture |
mclust |

library(dendextend)
dat <- effectsize::standardize(data[sapply(data, is.numeric)])
result <- pvclust::pvclust(dat, method.dist="euclidean", method.hclust="ward.D2", nboot=10, quiet=TRUE)
result %>%
as.dendrogram() %>%
sort() %>%
dendextend::pvclust_show_signif_gradient(result, signif_col_fun = grDevices::colorRampPalette(c("black", "red"))) %>%
dendextend::pvclust_show_signif(result, signif_value = c(2, 1)) %>%
dendextend::as.ggdend() %>%
ggplot2::ggplot(horiz=TRUE, offset_labels = -1)

References
LS0tDQp0aXRsZTogJyoqQ29uY2VwdHVhbCB2cy4gT2JzZXJ2ZWQgU3RydWN0dXJlIG9mIEhSViBpbmRpY2VzKionDQpzdWJ0aXRsZTogIkRhdGEgQW5hbGF5c2lzIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIHRoZW1lOiBjZXJ1bGVhbg0KICAgIGhpZ2hsaWdodDogcHlnbWVudHMNCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogMw0KICAgIHRvY19mbG9hdDogeWVzDQogICAgbnVtYmVyX3NlY3Rpb25zOiBubw0KICAgIGRmX3ByaW50OiBrYWJsZQ0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICB3b3JkX2RvY3VtZW50Og0KICAgIHJlZmVyZW5jZV9kb2N4OiB1dGlscy9UZW1wbGF0ZV9Xb3JkLmRvY3gNCiAgICBoaWdobGlnaHQ6IHB5Z21lbnRzDQogICAgdG9jOiBubw0KICAgIHRvY19kZXB0aDogMw0KICAgIGRmX3ByaW50OiBrYWJsZQ0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogIHJtYXJrZG93bjo6aHRtbF92aWduZXR0ZToNCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogMw0KICBwZGZfZG9jdW1lbnQ6DQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6ICcyJw0KICAgIGxhdGV4X2VuZ2luZTogeGVsYXRleA0KZWRpdG9yX29wdGlvbnM6DQogIGNodW5rX291dHB1dF90eXBlOiBjb25zb2xlDQpiaWJsaW9ncmFwaHk6IHV0aWxzL2JpYmxpb2dyYXBoeS5iaWINCmNzbDogdXRpbHMvYXBhLmNzbA0KLS0tDQoNCg0KPCEtLSANCiEhISEgSU1QT1JUQU5UOiBydW4gYHNvdXJjZSgidXRpbHMvcmVuZGVyLlIiKWAgdG8gcHVibGlzaCBpbnN0ZWFkIG9mIGNsaWNraW5nIG9uICdLbml0Jw0KLS0+DQoNCmBgYHtyIHNldHVwLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPVRSVUUsIGluY2x1ZGU9RkFMU0V9DQojIFNldCB1cCB0aGUgZW52aXJvbm1lbnQgKG9yIHVzZSBsb2NhbCBhbHRlcm5hdGl2ZSBgc291cmNlKCJ1dGlscy9jb25maWcuUiIpYCkNCnNvdXJjZSgiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL1JlYWxpdHlCZW5kaW5nL1RlbXBsYXRlUmVzdWx0cy9tYWluL3V0aWxzL2NvbmZpZy5SIikgIA0KDQpmYXN0IDwtIEZBTFNFICAjIE1ha2UgdGhpcyBmYWxzZSB0byBza2lwIHRoZSBjaHVua3MNCg0KIyBTZXQgdGhlbWUNCmdncGxvdDI6OnRoZW1lX3NldChzZWU6OnRoZW1lX21vZGVybigpKQ0KDQpgYGANCg0KIyBJbnRyb2R1Y3Rpb24NCg0KYGBge3IgYmFkZ2VzLCBlY2hvPUZBTFNFLCBtZXNzYWdlPVRSVUUsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9J2FzaXMnfQ0KIyBUaGlzIGNodW5rIGlzIGEgYml0IGNvbXBsZXggc28gZG9uJ3Qgd29ycnkgYWJvdXQgaXQ6IGl0J3MgbWFkZSB0byBhZGQgYmFkZ2VzIHRvIHRoZSBIVE1MIHZlcnNpb25zDQojIE5PVEU6IFlvdSBoYXZlIHRvIHJlcGxhY2UgdGhlIGxpbmtzIGFjY29yZGluZ2x5IHRvIGhhdmUgd29ya2luZyAiYnV0dG9ucyIgb24gdGhlIEhUTUwgdmVyc2lvbnMNCmlmICgha25pdHI6OmlzX2xhdGV4X291dHB1dCgpICYmIGtuaXRyOjppc19odG1sX291dHB1dCgpKSB7DQogIGNhdCgiIVtCdWlsZF0oaHR0cHM6Ly9naXRodWIuY29tL1RhbS1QaGFtL0hSVlN0cnVjdHVyZS93b3JrZmxvd3MvQnVpbGQvYmFkZ2Uuc3ZnKQ0KICAgICAgWyFbV2Vic2l0ZV0oaHR0cHM6Ly9pbWcuc2hpZWxkcy5pby9iYWRnZS9yZXBvLVJlYWRtZS0yMTk2RjMpXShodHRwczovL2dpdGh1Yi5jb20vVGFtLVBoYW0vSFJWU3RydWN0dXJlKQ0KICAgICAgWyFbV2Vic2l0ZV0oaHR0cHM6Ly9pbWcuc2hpZWxkcy5pby9iYWRnZS92aXNpdC13ZWJzaXRlLUU5MUU2MyldKGh0dHBzOi8vcmVhbGl0eWJlbmRpbmcuZ2l0aHViLmlvL1RlbXBsYXRlUmVzdWx0cy8pDQogICAgICBbIVtXZWJzaXRlXShodHRwczovL2ltZy5zaGllbGRzLmlvL2JhZGdlL2Rvd25sb2FkLS5kb2N4LUZGNTcyMildKGh0dHBzOi8vZ2l0aHViLmNvbS9SZWFsaXR5QmVuZGluZy9UZW1wbGF0ZVJlc3VsdHMvcmF3L21haW4vd29yZF9hbmRfcGRmL1N1cHBsZW1lbnRhcnlNYXRlcmlhbHMuZG9jeCkNCiAgICAgIFshW1dlYnNpdGVdKGh0dHBzOi8vaW1nLnNoaWVsZHMuaW8vYmFkZ2Uvc2VlLS5wZGYtRkY5ODAwKV0oaHR0cHM6Ly9naXRodWIuY29tL1JlYWxpdHlCZW5kaW5nL1RlbXBsYXRlUmVzdWx0cy9ibG9iL21haW4vd29yZF9hbmRfcGRmL1N1cHBsZW1lbnRhcnlNYXRlcmlhbHMucGRmKSIpDQp9DQpgYGANCg0KIyMgSW50cm9kdWN0aW9uDQoNClRoZSBhaW0gb2YgdGhpcyBzdHVkeSBpcyB0byBleHBsb3JlIHRoZSBmYWN0b3Igc3RydWN0dXJlIG9mIEhSViBpbmRpY2VzLg0KDQoNCiMjIERhdGFiYXNlcw0KDQojIyMgR2xhc2dvdyBVbml2ZXJzaXR5IERhdGFiYXNlDQoNClRoZSBHVURCIERhdGFiYXNlIFtAaG93ZWxsMjAxOGhpZ2hdIGNvbnRhaW5zIEVDR3MgZnJvbSAyNSBzdWJqZWN0cy4gRWFjaCBzdWJqZWN0IHdhcyByZWNvcmRlZCBwZXJmb3JtaW5nIDUgZGlmZmVyZW50IHRhc2tzIGZvciB0d28gbWludXRlcyAoc2l0dGluZywgZG9pbmcgYSBtYXRocyB0ZXN0IG9uIGEgdGFibGV0LCB3YWxraW5nIG9uIGEgdHJlYWRtaWxsLCBydW5uaW5nIG9uIGEgdHJlYWRtaWxsLCB1c2luZyBhIGhhbmQgYmlrZSkuIFRoZSBzYW1wbGluZyByYXRlIGlzIDI1MEh6IGZvciBhbGwgdGhlIGNvbmRpdGlvbnMuDQoNClRoZSBzY3JpcHQgdG8gZG93bmxvYWQgYW5kIGZvcm1hdCB0aGUgZGF0YWJhc2UgdXNpbmcgdGhlIFsqKkVDRy1HVURCKipdKGh0dHBzOi8vZ2l0aHViLmNvbS9iZXJuZHBvcnIvRUNHLUdVREIpIFB5dGhvbiBwYWNrYWdlIGJ5IEJlcm5kIFBvcnIgY2FuIGJlIGZvdW5kIFsqKmhlcmUqKl0oaHR0cHM6Ly9naXRodWIuY29tL25ldXJvcHN5Y2hvbG9neS9OZXVyb0tpdC9ibG9iL2Rldi9kYXRhL2d1ZGIvZG93bmxvYWRfZ3VkYi5weSkuDQoNCiMjIyBNSVQtQklIIEFycmh5dGhtaWEgRGF0YWJhc2UNCg0KVGhlIE1JVC1CSUggQXJyaHl0aG1pYSBEYXRhYmFzZSBbTUlULUFycmh5dGhtaWE7IEBtb29keTIwMDFpbXBhY3RdIGNvbnRhaW5zIDQ4IGV4Y2VycHRzIG9mIDMwLW1pbiBvZiB0d28tY2hhbm5lbCBhbWJ1bGF0b3J5IEVDRyByZWNvcmRpbmdzIHNhbXBsZWQgYXQgMzYwSHogYW5kIDI1IGFkZGl0aW9uYWwgcmVjb3JkaW5ncyBmcm9tIHRoZSBzYW1lIHBhcnRpY2lwYW50cyBpbmNsdWRpbmcgY29tbW9uIGJ1dCBjbGluaWNhbGx5IHNpZ25pZmljYW50IGFycmh5dGhtaWFzIChkZW5vdGVkIGFzIHRoZSBgTUlULUFycmh5dGhtaWEteGAgZGF0YWJhc2UpLg0KDQpUaGUgc2NyaXB0IHRvIGRvd25sb2FkIGFuZCBmb3JtYXQgdGhlIGRhdGFiYXNlIHVzaW5nIHRoZSBjYW4gYmUgZm91bmQgWyoqaGVyZSoqXShodHRwczovL2dpdGh1Yi5jb20vbmV1cm9wc3ljaG9sb2d5L05ldXJvS2l0L2Jsb2IvZGV2L2RhdGEvbWl0X2Fycmh5dGhtaWEvZG93bmxvYWRfbWl0X2Fycmh5dGhtaWEucHkpLg0KDQoNCiMjIyBNSVQtQklIIE5vcm1hbCBTaW51cyBSaHl0aG0gRGF0YWJhc2UNCg0KVGhpcyBkYXRhYmFzZSBpbmNsdWRlcyAxOCBjbGVhbiBsb25nLXRlcm0gRUNHIHJlY29yZGluZ3Mgb2Ygc3ViamVjdHMuIER1ZSB0byBtZW1vcnkgbGltaXRzLCB3ZSBvbmx5IGtlcHQgdGhlIHNlY29uZCBob3VyIG9mIHJlY29yZGluZyBvZiBlYWNoIHBhcnRpY2lwYW50Lg0KDQpUaGUgc2NyaXB0IHRvIGRvd25sb2FkIGFuZCBmb3JtYXQgdGhlIGRhdGFiYXNlIHVzaW5nIHRoZSBjYW4gYmUgZm91bmQgWyoqaGVyZSoqXShodHRwczovL2dpdGh1Yi5jb20vbmV1cm9wc3ljaG9sb2d5L05ldXJvS2l0L2Jsb2IvZGV2L2RhdGEvbWl0X25vcm1hbC9kb3dubG9hZF9taXRfbm9ybWFsLnB5KS4NCg0KDQo8IS0tICMjIyBMb2JhY2hldnNreSBVbml2ZXJzaXR5IEVsZWN0cm9jYXJkaW9ncmFwaHkgRGF0YWJhc2UgLS0+DQoNCjwhLS0gVGhlIExvYmFjaGV2c2t5IFVuaXZlcnNpdHkgRWxlY3Ryb2NhcmRpb2dyYXBoeSBEYXRhYmFzZSBbTFVEQjsgQGthbHlha3VsaW5hMjAxOGx1XSBjb25zaXN0cyBvZiAyMDAgMTAtc2Vjb25kIDEyLWxlYWQgRUNHIHNpZ25hbCByZWNvcmRzIHJlcHJlc2VudGluZyBkaWZmZXJlbnQgbW9ycGhvbG9naWVzIG9mIHRoZSBFQ0cgc2lnbmFsLiBUaGUgRUNHcyB3ZXJlIGNvbGxlY3RlZCBmcm9tIGhlYWx0aHkgdm9sdW50ZWVycyBhbmQgcGF0aWVudHMsIHdoaWNoIGhhZCB2YXJpb3VzIGNhcmRpb3Zhc2N1bGFyIGRpc2Vhc2VzLiBUaGUgYm91bmRhcmllcyBvZiBQLCBUIHdhdmVzIGFuZCBRUlMgY29tcGxleGVzIHdlcmUgbWFudWFsbHkgYW5ub3RhdGVkIGJ5IGNhcmRpb2xvZ2lzdHMgZm9yIGFsbCAyMDAgcmVjb3Jkcy4gLS0+DQoNCiMjIyBGYW50YXNpYSBEYXRhYmFzZQ0KDQpUaGUgRmFudGFzaWEgZGF0YWJhc2UgW0BpeWVuZ2FyMTk5NmFnZV0gY29uc2lzdHMgb2YgdHdlbnR5IHlvdW5nIGFuZCB0d2VudHkgZWxkZXJseSBoZWFsdGh5IHN1YmplY3RzLiBBbGwgc3ViamVjdHMgcmVtYWluZWQgaW4gYSByZXN0aW5nIHN0YXRlIGluIHNpbnVzIHJoeXRobSB3aGlsZSB3YXRjaGluZyB0aGUgbW92aWUgRmFudGFzaWEgKERpc25leSwgMTk0MCkgdG8gaGVscCBtYWludGFpbiB3YWtlZnVsbmVzcy4gVGhlIGNvbnRpbnVvdXMgRUNHIHNpZ25hbHMgd2VyZSBkaWdpdGl6ZWQgYXQgMjUwIEh6LiBFYWNoIGhlYXJ0YmVhdCB3YXMgYW5ub3RhdGVkIHVzaW5nIGFuIGF1dG9tYXRlZCBhcnJoeXRobWlhIGRldGVjdGlvbiBhbGdvcml0aG0sIGFuZCBlYWNoIGJlYXQgYW5ub3RhdGlvbiB3YXMgdmVyaWZpZWQgYnkgdmlzdWFsIGluc3BlY3Rpb24uDQoNCg0KDQojIyBQcm9jZWR1cmUNCg0KYGBge3B5dGhvbiwgZXZhbD1GQUxTRSwgZWNobz1GQUxTRX0NCmltcG9ydCBwYW5kYXMgYXMgcGQNCmltcG9ydCBudW1weSBhcyBucA0KaW1wb3J0IG5ldXJva2l0MiBhcyBuaw0KDQojIExvYWQgVHJ1ZSBSLXBlYWtzIGxvY2F0aW9uDQpkYXRhZmlsZXMgPSBbcGQucmVhZF9jc3YoIi4uLy4uL2RhdGEvZ3VkYi9ScGVha3MuY3N2IiksDQogICAgICAgICAgICAgcGQucmVhZF9jc3YoIi4uLy4uL2RhdGEvbWl0X2Fycmh5dGhtaWEvUnBlYWtzLmNzdiIpLA0KICAgICAgICAgICAgIHBkLnJlYWRfY3N2KCIuLi8uLi9kYXRhL21pdF9ub3JtYWwvUnBlYWtzLmNzdiIpLA0KICAgICAgICAgICAgIHBkLnJlYWRfY3N2KCIuLi8uLi9kYXRhL2ZhbnRhc2lhL1JwZWFrcy5jc3YiKV0NCg0KIyBHZXQgcmVzdWx0cw0KYWxsX3Jlc3VsdHMgPSBwZC5EYXRhRnJhbWUoKQ0KDQpmb3IgZmlsZSBpbiBkYXRhZmlsZXM6DQogICAgZm9yIGRhdGFiYXNlIGluIG5wLnVuaXF1ZShmaWxlWyJEYXRhYmFzZSJdKToNCiAgICAgICAgZGF0YSA9IGZpbGVbZmlsZVsiRGF0YWJhc2UiXSA9PSBkYXRhYmFzZV0NCiAgICAgICAgZm9yIHBhcnRpY2lwYW50IGluIG5wLnVuaXF1ZShkYXRhWyJQYXJ0aWNpcGFudCJdKToNCiAgICAgICAgICAgIGRhdGFfcGFydGljaXBhbnQgPSBkYXRhW2RhdGFbIlBhcnRpY2lwYW50Il0gPT0gcGFydGljaXBhbnRdDQogICAgICAgICAgICBzYW1wbGluZ19yYXRlID0gbnAudW5pcXVlKGRhdGFfcGFydGljaXBhbnRbIlNhbXBsaW5nX1JhdGUiXSlbMF0NCiAgICAgICAgICAgIHJwZWFrcyA9IGRhdGFfcGFydGljaXBhbnRbIlJwZWFrcyJdLnZhbHVlcw0KDQogICAgICAgICAgICByZXN1bHRzID0gbmsuaHJ2KHJwZWFrcywgc2FtcGxpbmdfcmF0ZT1zYW1wbGluZ19yYXRlKQ0KICAgICAgICAgICAgcmVzdWx0c1siUGFydGljaXBhbnQiXSA9IHBhcnRpY2lwYW50DQogICAgICAgICAgICByZXN1bHRzWyJEYXRhYmFzZSJdID0gZGF0YWJhc2UNCiAgICAgICAgICAgIHJlc3VsdHNbIlJlY29yZGluZ19MZW5ndGgiXSA9IHJwZWFrc1stMV0gLyBzYW1wbGluZ19yYXRlIC8gNjANCg0KICAgICAgICAgICAgYWxsX3Jlc3VsdHMgPSBwZC5jb25jYXQoW2FsbF9yZXN1bHRzLCByZXN1bHRzXSwgYXhpcz0wKQ0KDQphbGxfcmVzdWx0cy50b19jc3YoImRhdGEvZGF0YS5jc3YiLCBpbmRleD1GYWxzZSkNCmBgYA0KDQojIyBSZXN1bHRzDQoNCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGVhc3lzdGF0cykNCg0KZGF0YSA8LSByZWFkLmNzdigiZGF0YS9kYXRhLmNzdiIsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkgJT4lIA0KICBzZWxlY3QoLUhSVl9VTEYsIC1IUlZfVkxGKSAlPiUgICMgRW1wdHkNCiAgZmlsdGVyKERhdGFiYXNlICE9ICJMVURCIikgIyB0b28gc2hvcnQgcmVjb3JkaW5ncywgbWFueSBpbmRpY2VzIGRpZG4ndCBjb252ZXJnZQ0KbmFtZXMoZGF0YSkgPC0gc3RyaW5ncjo6c3RyX3JlbW92ZShuYW1lcyhkYXRhKSwgIkhSVl8iKQ0KYGBgDQoNCg0KIyMjIFJlZHVuZGFudCBJbmRpY2VzDQoNCiMjIyMgUmVtb3ZlIEVxdWl2YWxlbnQgKHIgaGlnaGVyIHRoYW4gLjk5NSkNCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy53aWR0aD0xNywgZmlnLmhlaWdodD0xN30NCmRhdGEgJT4lIA0KICBjb3JyZWxhdGlvbjo6Y29ycmVsYXRpb24oKSAlPiUgDQogIGZpbHRlcihhYnMocikgPiAwLjk5NSkgJT4lIA0KICBhcnJhbmdlKFBhcmFtZXRlcjEsIGRlc2MoYWJzKHIpKSkNCg0KZGF0YSA8LSBkYXRhICU+JSANCiAgc2VsZWN0KC1TRFNELCAtU0QxLCAtU0QxZCwgLVNEMWEsIC1DVlNEKSAlPiUgICMgU2FtZSBhcyBSTVNTRCANCiAgc2VsZWN0KC1TRE5OZCwgLVNETk5hKSAlPiUgICMgU2FtZSBhcyBTRE5ODQogIHNlbGVjdCgtU0QyZCwgLVNEMmEpICU+JSAgICMgU2FtZSBhcyBTRDINCiAgc2VsZWN0KC1DZCkgJT4lICAgIyBTYW1lIGFzIENhDQogIHNlbGVjdCgtQzFkLCAtQzJkKSAjIFNhbWUgYXMgQzFhIGFuZCBDMmENCmBgYA0KDQojIyMjIFJlbW92ZSBTdHJvbmdseSBDb3JyZWxhdGVkIChyIGhpZ2hlciB0aGFuIC45OCkNCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy53aWR0aD0xNywgZmlnLmhlaWdodD0xN30NCmRhdGEgJT4lIA0KICBjb3JyZWxhdGlvbjo6Y29ycmVsYXRpb24oKSAlPiUgDQogIGZpbHRlcihhYnMocikgPiAwLjk1KSAlPiUNCiAgYXJyYW5nZShQYXJhbWV0ZXIxLCBkZXNjKGFicyhyKSkpDQoNCmRhdGEgPC0gZGF0YSAlPiUgDQogIHNlbGVjdCgtR0ksIC1TSSkgJT4lICAjIFNhbWUgYXMgQUkgDQogIHNlbGVjdCgtU0QyKSAlPiUgICMgU2FtZSBhcyBTRE5ODQogIHNlbGVjdCgtTWVkaWFuTk4pICU+JSAgIyBTYW1lIGFzIE1lYW5OTg0KICBzZWxlY3QoLUlBTFMpICU+JSAgIyBTYW1lIGFzIFBJUA0KICBzZWxlY3QoLVNETk4sIC1DVk5OKSAjIFNhbWUgYXMgUk1TU0QNCmBgYA0KDQoNCg0KIyMjIFJlY29yZGluZyBMZW5ndGgNCg0KIyMjIyBJbnZlc3RpZ2F0ZSBlZmZlY3QNCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQ0KY29ycmVsYXRpb24oZGF0YSkgJT4lIA0KICBmaWx0ZXIoUGFyYW1ldGVyMiA9PSAiUmVjb3JkaW5nX0xlbmd0aCIpICU+JSANCiAgYXJyYW5nZShkZXNjKGFicyhyKSkpDQpgYGANCg0KIyMjIyBBZGp1c3QgdGhlIGRhdGEgZm9yIHJlY29yZGluZyBsZW5ndGgNCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQ0KZGF0YSA8LSBlZmZlY3RzaXplOjphZGp1c3QoZGF0YSwgZWZmZWN0PSJSZWNvcmRpbmdfTGVuZ3RoIikgJT4lIA0KICBzZWxlY3QoLVJlY29yZGluZ19MZW5ndGgpDQpgYGANCg0KIyMjIEdhdXNzaWFuIEdyYXBoaWNhbCBNb2RlbA0KDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcud2lkdGg9MTcsIGZpZy5oZWlnaHQ9MTd9DQpsaWJyYXJ5KGdncmFwaCkNCg0KZGF0YSAlPiUgDQogIGNvcnJlbGF0aW9uOjpjb3JyZWxhdGlvbihwYXJ0aWFsPUZBTFNFKSAlPiUgDQogIGNvcnJlbGF0aW9uOjpjb3JfdG9fcGNvcigpICU+JSANCiAgZmlsdGVyKGFicyhyKSA+IDAuMikgJT4lDQogIHRpZHlncmFwaDo6YXNfdGJsX2dyYXBoKGRpcmVjdGVkPUZBTFNFKSAlPiUgDQogIGRwbHlyOjptdXRhdGUoY2xvc2VuZXNzID0gdGlkeWdyYXBoOjpjZW50cmFsaXR5X2Nsb3NlbmVzcyhub3JtYWxpemVkID0gVFJVRSksDQogICAgICAgICAgICAgICAgZGVncmVlID0gdGlkeWdyYXBoOjpjZW50cmFsaXR5X2RlZ3JlZShub3JtYWxpemVkID0gVFJVRSksDQogICAgICAgICAgICAgICAgYmV0d2VlbmVzcyA9IHRpZHlncmFwaDo6Y2VudHJhbGl0eV9iZXR3ZWVubmVzcyhub3JtYWxpemVkID0gVFJVRSkpICU+JQ0KICB0aWR5Z3JhcGg6OmFjdGl2YXRlKG5vZGVzKSAlPiUNCiAgZHBseXI6Om11dGF0ZShncm91cDEgPSBhcy5mYWN0b3IodGlkeWdyYXBoOjpncm91cF9lZGdlX2JldHdlZW5uZXNzKCkpLA0KICAgICAgICAgICAgICAgICMgZ3JvdXAyID0gYXMuZmFjdG9yKHRpZHlncmFwaDo6Z3JvdXBfb3B0aW1hbCgpKSwNCiAgICAgICAgICAgICAgICAjIGdyb3VwMyA9IGFzLmZhY3Rvcih0aWR5Z3JhcGg6Omdyb3VwX3dhbGt0cmFwKCkpLA0KICAgICAgICAgICAgICAgICMgZ3JvdXA0ID0gYXMuZmFjdG9yKHRpZHlncmFwaDo6Z3JvdXBfc3BpbmdsYXNzKCkpLA0KICAgICAgICAgICAgICAgIGdyb3VwNSA9IGFzLmZhY3Rvcih0aWR5Z3JhcGg6Omdyb3VwX2xvdXZhaW4oKSkpICU+JSANCiAgZ2dyYXBoOjpnZ3JhcGgobGF5b3V0ID0gImZyIikgKw0KICAgIGdncmFwaDo6Z2VvbV9lZGdlX2FyYyhhZXMoY29sb3VyID0gciwgZWRnZV93aWR0aCA9IGFicyhyKSksIHN0cmVuZ3RoID0gMC4xLCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArDQogICAgZ2dyYXBoOjpnZW9tX25vZGVfcG9pbnQoYWVzKHNpemUgPSBkZWdyZWUsIGNvbG9yID0gZ3JvdXA1KSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKw0KICAgIGdncmFwaDo6Z2VvbV9ub2RlX3RleHQoYWVzKGxhYmVsID0gbmFtZSksIGNvbG91ciA9ICJ3aGl0ZSIpICsNCiAgICBnZ3JhcGg6OnNjYWxlX2VkZ2VfY29sb3JfZ3JhZGllbnQyKGxvdyA9ICIjYTIwMDI1IiwgaGlnaCA9ICIjMDA4YTAwIiwgbmFtZSA9ICJyIikgKw0KICAgIGdncmFwaDo6dGhlbWVfZ3JhcGgoKSArDQogICAgZ3VpZGVzKGVkZ2Vfd2lkdGggPSBGQUxTRSkgKw0KICAgIHNjYWxlX3hfY29udGludW91cyhleHBhbmQgPSBleHBhbnNpb24oYyguMTAsIC4xMCkpKSArDQogICAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGV4cGFuc2lvbihjKC4xMCwgLjEwKSkpICsNCiAgICBzY2FsZV9zaXplX2NvbnRpbnVvdXMocmFuZ2UgPSBjKDIwLCAzMCkpICsNCiAgICBzY2FsZV9lZGdlX3dpZHRoX2NvbnRpbnVvdXMocmFuZ2UgPSBjKDAuNSwgMikpICsNCiAgICBzZWU6OnNjYWxlX2NvbG9yX21hdGVyaWFsX2QocGFsZXR0ZT0icmFpbmJvdyIsIHJldmVyc2U9VFJVRSkNCmBgYA0KDQpHcm91cHMgd2VyZSBpZGVudGlmaWVkIHVzaW5nIHRoZSBbdGlkeWdyYXBoOjpncm91cF9vcHRpbWFsXShodHRwczovL3JkcnIuaW8vY3Jhbi90aWR5Z3JhcGgvbWFuL2dyb3VwX2dyYXBoLmh0bWwpIGFsZ29yaXRobS4NCg0KDQojIyMgRmFjdG9yIEFuYWx5c2lzDQoNCg0KIyMjIyBIb3cgbWFueSBmYWN0b3JzIA0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmNvciA8LSBjb3JyZWxhdGlvbjo6Y29ycmVsYXRpb24oZGF0YVtzYXBwbHkoZGF0YSwgaXMubnVtZXJpYyldKSAlPiUgDQogIGFzLm1hdHJpeCgpDQoNCm4gPC0gcGFyYW1ldGVyczo6bl9mYWN0b3JzKGRhdGEsIGNvcj1jb3IpDQoNCm4NCg0KcGxvdChuKSArDQogIHNlZTo6dGhlbWVfbW9kZXJuKCkNCmBgYA0KDQojIyMjIEV4cGxvcmF0b3J5IEZhY3RvciBBbmFseXNpcyAoRUZBKQ0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLndpZHRoPTE1LCBmaWcuaGVpZ2h0PTE1fQ0KZWZhIDwtIHBhcmFtZXRlcnM6OmZhY3Rvcl9hbmFseXNpcyhkYXRhLCBjb3I9Y29yLCBuPTcsIHJvdGF0aW9uPSJ2YXJpbWF4IiwgZm09Im1sIikNCg0KcHJpbnQoZWZhLCB0aHJlc2hvbGQ9Im1heCIsIHNvcnQ9VFJVRSkNCg0KcGxvdChlZmEpICsNCiAgc2VlOjp0aGVtZV9tb2Rlcm4oKQ0KYGBgDQoNCiMjIyMgQ29uZmlybWF0b3J5IEZhY3RvciBBbmFseXNpcyAoQ0ZBKQ0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLndpZHRoPTE1LCBmaWcuaGVpZ2h0PTE1fQ0KbGlicmFyeShsYXZhYW4pDQoNCm1vZGVsIDwtIHBhcmFtZXRlcnM6OmVmYV90b19jZmEoZWZhLCB0aHJlc2hvbGQgPSAibWF4IikNCmNmYSA8LSBsYXZhYW46OmNmYShtb2RlbCwgZGF0YT1kYXRhKSAlPiUNCiAgcGFyYW1ldGVyczo6cGFyYW1ldGVycyhzdGFuZGFyZGl6ZT1UUlVFKQ0KDQpjZmENCg0KcGxvdChjZmEpDQpgYGANCg0KDQojIyMgQ2x1c3RlciBBbmFseXNpcw0KDQojIyMjIEhvdyBtYW55IGNsdXN0ZXJzDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KZGF0IDwtIGVmZmVjdHNpemU6OnN0YW5kYXJkaXplKGRhdGFbc2FwcGx5KGRhdGEsIGlzLm51bWVyaWMpXSkNCg0KbiA8LSBwYXJhbWV0ZXJzOjpuX2NsdXN0ZXJzKHQoZGF0KSwgcGFja2FnZSA9IGMoIm1jbHVzdCIsICJjbHVzdGVyIikpDQoNCm4NCg0KcGxvdChuKSArDQogIHRoZW1lX21vZGVybigpDQpgYGANCg0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9MTB9DQpsaWJyYXJ5KGRlbmRleHRlbmQpDQoNCmRhdCA8LSBlZmZlY3RzaXplOjpzdGFuZGFyZGl6ZShkYXRhW3NhcHBseShkYXRhLCBpcy5udW1lcmljKV0pDQoNCnJlc3VsdCA8LSBwdmNsdXN0OjpwdmNsdXN0KGRhdCwgbWV0aG9kLmRpc3Q9ImV1Y2xpZGVhbiIsIG1ldGhvZC5oY2x1c3Q9IndhcmQuRDIiLCBuYm9vdD0xMCwgcXVpZXQ9VFJVRSkNCg0KcmVzdWx0ICU+JSANCiAgYXMuZGVuZHJvZ3JhbSgpICU+JSANCiAgc29ydCgpICU+JSANCiAgZGVuZGV4dGVuZDo6cHZjbHVzdF9zaG93X3NpZ25pZl9ncmFkaWVudChyZXN1bHQsIHNpZ25pZl9jb2xfZnVuID0gZ3JEZXZpY2VzOjpjb2xvclJhbXBQYWxldHRlKGMoImJsYWNrIiwgInJlZCIpKSkgJT4lIA0KICBkZW5kZXh0ZW5kOjpwdmNsdXN0X3Nob3dfc2lnbmlmKHJlc3VsdCwgc2lnbmlmX3ZhbHVlID0gYygyLCAxKSkgJT4lDQogIGRlbmRleHRlbmQ6OmFzLmdnZGVuZCgpICU+JSANCiAgZ2dwbG90Mjo6Z2dwbG90KGhvcml6PVRSVUUsIG9mZnNldF9sYWJlbHMgPSAtMSkNCmBgYA0KDQoNCg0KIyMgUmVmZXJlbmNlcw0K